{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "text.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.5"
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DweYe9FcbMK_"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "AVV2e0XKbJeX",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "sUtoed20cRJJ"
      },
      "source": [
        "# tf.data を使ったテキストの読み込み"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1ap_W4aQcgNT"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/load_data/text\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ja/tutorials/load_data/text.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ja/tutorials/load_data/text.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RSywPQ2n736s"
      },
      "source": [
        "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "NWeQAo0Ec_BL"
      },
      "source": [
        "このチュートリアルでは、`tf.data.TextLineDataset` を使ってテキストファイルからサンプルを読み込む方法を例示します。`TextLineDataset` は、テキストファイルからデータセットを作成するために設計されています。この中では、元のテキストファイルの一行一行がサンプルです。これは、（たとえば、詩やエラーログのような）基本的に行ベースのテキストデータを扱うのに便利でしょう。\n",
        "\n",
        "このチュートリアルでは、おなじ作品であるホーマーのイリアッドの異なる 3 つの英語翻訳版を使い、テキスト 1 行から翻訳者を特定するモデルを訓練します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fgZ9gjmPfSnK"
      },
      "source": [
        "## 設定"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "baYFZMW_bJHh",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "!pip install tensorflow-gpu==2.0.0-rc1\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "import tensorflow_datasets as tfds\n",
        "import os"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "YWVWjyIkffau"
      },
      "source": [
        "3 つの翻訳のテキストは次のとおりです。\n",
        "\n",
        " - [William Cowper](https://en.wikipedia.org/wiki/William_Cowper) — [text](https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt)\n",
        "\n",
        " - [Edward, Earl of Derby](https://en.wikipedia.org/wiki/Edward_Smith-Stanley,_14th_Earl_of_Derby) — [text](https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt)\n",
        "\n",
        "- [Samuel Butler](https://en.wikipedia.org/wiki/Samuel_Butler_%28novelist%29) — [text](https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt)\n",
        "\n",
        "このチュートリアルで使われているテキストファイルは、ヘッダ、フッタ、行番号、章のタイトルの削除など、いくつかの典型的な前処理を行ったものです。前処理後のファイルをダウンロードしましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "4YlKQthEYlFw",
        "colab": {}
      },
      "source": [
        "DIRECTORY_URL = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/'\n",
        "FILE_NAMES = ['cowper.txt', 'derby.txt', 'butler.txt']\n",
        "\n",
        "for name in FILE_NAMES:\n",
        "  text_dir = tf.keras.utils.get_file(name, origin=DIRECTORY_URL+name)\n",
        "  \n",
        "parent_dir = os.path.dirname(text_dir)\n",
        "\n",
        "parent_dir"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "q3sDy6nuXoNp"
      },
      "source": [
        "## テキストをデータセットに読み込む\n",
        "\n",
        "ファイルをイテレートし、それぞれを別々のデータセットに読み込みます。\n",
        "\n",
        "サンプルはそれぞれにラベル付けが必要なので、ラベル付け関数を適用するために `tf.data.Dataset.map` を使います。このメソッドは、データセット中のすべてのサンプルをイテレートし、(`example, label`)というペアを返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "K0BjCOpOh7Ch",
        "colab": {}
      },
      "source": [
        "def labeler(example, index):\n",
        "  return example, tf.cast(index, tf.int64)  \n",
        "\n",
        "labeled_data_sets = []\n",
        "\n",
        "for i, file_name in enumerate(FILE_NAMES):\n",
        "  lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name))\n",
        "  labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i))\n",
        "  labeled_data_sets.append(labeled_dataset)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "M8PHK5J_cXE5"
      },
      "source": [
        "ラベル付けの終わったデータセットを結合して一つのデータセットにし、シャッフルします。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6jAeYkTIi9-2",
        "colab": {}
      },
      "source": [
        "BUFFER_SIZE = 50000\n",
        "BATCH_SIZE = 64\n",
        "TAKE_SIZE = 5000"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Qd544E-Sh63L",
        "colab": {}
      },
      "source": [
        "all_labeled_data = labeled_data_sets[0]\n",
        "for labeled_dataset in labeled_data_sets[1:]:\n",
        "  all_labeled_data = all_labeled_data.concatenate(labeled_dataset)\n",
        "  \n",
        "all_labeled_data = all_labeled_data.shuffle(\n",
        "    BUFFER_SIZE, reshuffle_each_iteration=False)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "r4JEHrJXeG5k"
      },
      "source": [
        "`tf.data.Dataset.take` と `print` を使って、`(example, label)` のペアがどのようなものかを見ることができます。`numpy` プロパティがそれぞれのテンソルの値を示します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gywKlN0xh6u5",
        "colab": {}
      },
      "source": [
        "for ex in all_labeled_data.take(5):\n",
        "  print(ex)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5rrpU2_sfDh0"
      },
      "source": [
        "## テキスト行を数字にエンコードする\n",
        "\n",
        "機械学習モデルが扱うのは単語ではなくて数字であるため、文字列は数字のリストに変換する必要があります。このため、一意の単語を一意の数字にマッピングします。\n",
        "\n",
        "### ボキャブラリーの構築\n",
        "\n",
        "まず最初に、テキストをトークン化し、個々の一意な単語の集まりとして、ボキャブラリーを構築します。これを行うには、TensorFlow やPython を使ういくつかの方法があります。ここでは次のようにします。\n",
        "\n",
        "1. 各サンプルの `numpy` 値をイテレートします。\n",
        "2. `tfds.features.text.Tokenizer` を使って、それをトークンに分割します。\n",
        "3. 重複を排除するため、トークンを Python の集合に集約します。\n",
        "4. あとで使用するため、ボキャブラリーのサイズを取得します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "YkHtbGnDh6mg",
        "colab": {}
      },
      "source": [
        "tokenizer = tfds.features.text.Tokenizer()\n",
        "\n",
        "vocabulary_set = set()\n",
        "for text_tensor, _ in all_labeled_data:\n",
        "  some_tokens = tokenizer.tokenize(text_tensor.numpy())\n",
        "  vocabulary_set.update(some_tokens)\n",
        "\n",
        "vocab_size = len(vocabulary_set)\n",
        "vocab_size"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "0W35VJqAh9zs"
      },
      "source": [
        "### サンプルをエンコードする\n",
        "\n",
        "`vocabulary_set` を `tfds.features.text.TokenTextEncoder` に渡してエンコーダーを作成します。エンコーダーの `encode` メソッドは、テキスト文字列を引数にとり、整数のリストを返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gkxJIVAth6j0",
        "colab": {}
      },
      "source": [
        "encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "v6S5Qyabi-vo"
      },
      "source": [
        "1行だけにこれを適用し、出力がどの様になるか確かめることができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "jgxPZaxUuTbk",
        "colab": {}
      },
      "source": [
        "example_text = next(iter(all_labeled_data))[0].numpy()\n",
        "print(example_text)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XoVpKR3qj5yb",
        "colab": {}
      },
      "source": [
        "encoded_example = encoder.encode(example_text)\n",
        "print(encoded_example)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "p9qHM0v8k_Mg"
      },
      "source": [
        "次に、このエンコーダーを `tf.py_function` でラッピングして、データセットの `map` メソッドに渡し、データセットに適用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "HcIQ7LOTh6eT",
        "colab": {}
      },
      "source": [
        "def encode(text_tensor, label):\n",
        "  encoded_text = encoder.encode(text_tensor.numpy())\n",
        "  return encoded_text, label\n",
        "\n",
        "def encode_map_fn(text, label):\n",
        "  return tf.py_function(encode, inp=[text, label], Tout=(tf.int64, tf.int64))\n",
        "\n",
        "all_encoded_data = all_labeled_data.map(encode_map_fn)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_YZToSXSm0qr"
      },
      "source": [
        "## データセットを、テスト用と訓練用のバッチに分割する\n",
        "\n",
        "`tf.data.Dataset.take`と`tf.data.Dataset.skip`を使って、小さなテスト用データセットと、より大きな訓練用セットを作成します。\n",
        "\n",
        "モデルに渡す前に、データセットをバッチ化する必要があります。通常、バッチの中のサンプルはおなじサイズと形状である必要があります。しかし、これらのデータセットの中のサンプルはすべておなじサイズではありません。テキストの各行の単語数は異なっています。このため、（`batch`の代わりに）`tf.data.Dataset.padded_batch` メソッドを使ってサンプルをおなじサイズにパディングします。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "r-rmbijQh6bf",
        "colab": {}
      },
      "source": [
        "train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)\n",
        "train_data = train_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[]))\n",
        "\n",
        "test_data = all_encoded_data.take(TAKE_SIZE)\n",
        "test_data = test_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Xdz7SVwmqi1l"
      },
      "source": [
        "もう、`test_data` と `train_data` は、(`example, label`)というペアのコレクションではなく、バッチのコレクションです。それぞれのバッチは、（**たくさんのサンプル**, **たくさんのラベル**）という配列のペアです。\n",
        "\n",
        "見てみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "kMslWfuwoqpB",
        "colab": {}
      },
      "source": [
        "sample_text, sample_labels = next(iter(test_data))\n",
        "\n",
        "sample_text[0], sample_labels[0]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UI4I6_Sa0vWu"
      },
      "source": [
        "（ゼロをパディングに使用した）新しいトークン番号を1つ導入したので、ボキャブラリーサイズは1つ増えています。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IlD1Lli91vuc",
        "colab": {}
      },
      "source": [
        "vocab_size += 1"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "K8SUhGFNsmRi"
      },
      "source": [
        "## モデルを構築する"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "QJgI1pow2YR9",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wi0iiKLTKdoF"
      },
      "source": [
        "最初の層は、整数表現を密なベクトル埋め込みに変換します。詳細は[単語埋め込み](../../tutorials/sequences/word_embeddings)のチュートリアルを参照ください。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "DR6-ctbY638P",
        "colab": {}
      },
      "source": [
        "model.add(tf.keras.layers.Embedding(vocab_size, 64))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_8OJOPohKh1q"
      },
      "source": [
        "次の層は[Long Short-Term Memory](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) 層です。この層により、モデルは単語をほかの単語の文脈の中で解釈します。LSTM の Bidirectional ラッパーにより、データポイントを、その前とその後のデータポイントとの関連で学習することができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "x6rnq6DN_WUs",
        "colab": {}
      },
      "source": [
        "model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "cdffbMr5LF1g"
      },
      "source": [
        "最後に、一つ以上の全結合層があり、最後の層は出力層です。出力層はラベルすべての確率を生成します。もっと複雑なも確率の高いラベルが、モデルが予測するサンプルのラベルです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "QTEaNSnLCsv5",
        "colab": {}
      },
      "source": [
        "# 1 つ以上の Dense 層\n",
        "# `for` 行の中のリストを編集して、層のサイズの実験をしてください\n",
        "for units in [64, 64]:\n",
        "  model.add(tf.keras.layers.Dense(units, activation='relu'))\n",
        "\n",
        "# 出力層 最初の引数はラベルの数\n",
        "model.add(tf.keras.layers.Dense(3, activation='softmax'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zLHPU8q5DLi_"
      },
      "source": [
        "最後にモデルをコンパイルします。ソフトマックスによるカテゴリー分類モデルでは、損失関数として `sparse_categorical_crossentropy` を使用します。ほかのオプティマイザを使うこともできますが、`adam` がよく使われます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "pkTBUVO4h6Y5",
        "colab": {}
      },
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss='sparse_categorical_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DM-HLo5NDhql"
      },
      "source": [
        "## モデルを訓練する\n",
        "\n",
        "このモデルをこのデータに適用すると（約83%の）まともな結果が得られます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "aLtO33tNh6V8",
        "colab": {}
      },
      "source": [
        "model.fit(train_data, epochs=3, validation_data=test_data)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KTPCYf_Jh6TH",
        "colab": {}
      },
      "source": [
        "eval_loss, eval_acc = model.evaluate(test_data)\n",
        "\n",
        "print('\\nEval loss: {}, Eval accuracy: {}'.format(eval_loss, eval_acc))"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
